#include <Player.h>
#include <string>
#include <vector>
#include <random>
#include <Level.h>

using namespace std;

int gameTick = 0;

float Vector2::returnLength() {
    return hypot(abs(x), abs(y));
}
void Vector2::normalize() {
    if (abs(x) < __FLT_EPSILON__ && abs(y) < __FLT_EPSILON__) {
        x = 0.0f; y = 0.0f; return;
    }
    float length = returnLength();
    x /= length;
    y /= length;
}

void Vector2::add(Vector2 other) {
    x += other.x;
    y += other.y;
}

void Vector2::sub(Vector2 other) {
    x -= other.x;
    y -= other.y;
}

void Vector2::mult(float ratio)
{
    x *= ratio;
    y *= ratio;
}

float Vector2::distance(const Vector2& other) {
    float xOffset = abs(other.x - x);
    float yOffset = abs(other.y - y);
    float distance = hypot(xOffset, yOffset);
    return distance;
}

void Vector2::turn(const float turnAngle) {
    float length = returnLength();
    float currAngle = vectorToAngle(x, y);
    currAngle += turnAngle;
    x = cos(currAngle) * length;
    y = sin(currAngle) * length;
}

void Player::fillPlayer(string playerName, string uuid, Vector2 pos, Weapon weapon) {
    this->playerName = playerName;
    this->uuid = uuid;
    this->pos = pos;
    velocity = Vector2 {0, 0};
    this->weapon = weapon;
    mousePos = Vector2 {0, 0};
}

void Player::printPlayerInfo() {
    printf("%s(%s) -> pos:(%f, %f); velocity:(%f, %f); hitPoints:%f; angle:(%f); grazeColdDown:(%f) \n",
    playerName.c_str(), uuid.c_str(), pos.x, pos.y, velocity.x, velocity.y, hitPoints, angle, grazeColdDown);
}

/*
    "reimu_spellcard",
    "marisa_star",
    "reimilia_arrow",
    "cirno_ice",
    "alice_doll",
    "keine_mirror",
    "mokou_flame",
    "sanae_star",
    "koishi_love",
    "murasa_anchor",
    "seija_arrow",
    "doremy_catcher",
    "okina_star",
    "yachie_shield",
    "zanmu_ghost"
*/

vector<string> weaponList = {
    "reimu_spellcard",
    "marisa_star",
    "reimilia_arrow",
    "cirno_ice",
    "alice_doll",
    "keine_mirror",
    "mokou_flame",
    "sanae_star",
    "koishi_love",
    "murasa_anchor",
    "seija_arrow",
    "doremy_catcher",
    "okina_star",
    "yachie_shield",
    "zanmu_ghost"
};

void Player::Update(const float rate, queue<LevelAction>& responseBuffer) {
    Vector2 moveDirection { 0, 0 };
    moveDirection.y -= keyWPressed ? 1 : 0;
    moveDirection.y += keySPressed ? 1 : 0;
    moveDirection.x -= keyAPressed ? 1 : 0;
    moveDirection.x += keyDPressed ? 1 : 0;
    moveDirection.normalize();
    moveDirection.mult((moveStrength - friction) / frictionRate);
    if (grazeColdDown < 0.3f * grazeMaxColdDown) {
        velocity.x = moveDirection.x;
        velocity.y = moveDirection.y;
        // 冲刺后一段时间时不允许主动操作
    }
    if (hitPoints <= __FLT_EPSILON__) {
        velocity.x = 0;
        velocity.y = 0;
    }
    // 玩家移动加速度
    /*
    float currFriction = (friction +
        frictionRate * velocity.returnLength()) * rate;
    if (currFriction >= velocity.returnLength()) {
        velocity = Vector2 { 0, 0 };
    }
    else {
        float currVelocityLength = velocity.returnLength() - currFriction;
        velocity.normalize();
        velocity.mult(currVelocityLength);
    }
    */
    // 玩家摩擦加速度
    pos.x += velocity.x * rate;
    pos.y += velocity.y * rate;
    // 玩家位置更新（还有边界判断）
    angle = vectorToAngle(mousePos.x - pos.x,
        mousePos.y - pos.y);
    // 计算角度信息并传入
    vector<string> posInfo { uuid, "pa",
        to_string(pos.x), to_string(pos.y), to_string(angle) };
    pushToResponseBuffer("pla", posInfo, responseBuffer);
    // 传入位置信息
    grazeColdDown = (grazeColdDown < rate) ? 0.0f : (grazeColdDown - rate);
    /*
    if (mouseRightPressed && grazeColdDown < rate && hitPoints >= __FLT_EPSILON__) {
        Vector2 swiftPos = Vector2 { mousePos.x - pos.x, mousePos.y - pos.y };
        swiftPos.normalize();
        swiftPos.mult(grazeVelocity);
        velocity = Vector2 { velocity.x + swiftPos.x, velocity.y + swiftPos.y };
        grazeColdDown = grazeMaxColdDown;
        vector<string> burstInfo { uuid, "graze_burst" };
        pushToResponseBuffer("particle_burst", burstInfo, responseBuffer);
    }
    */
    // 玩家擦弹处理（注意：虽然是擦弹，但是不要往子弹上撞，会死）
    bool respawn = false;
    if (hitPoints < __FLT_EPSILON__) {
        recovery += rate;
        if (gameTick % 60 == 0) {
            vector<string> recoveryInfo { uuid, "r", to_string(maxRecovery - recovery) };
            pushToResponseBuffer("pla", recoveryInfo, responseBuffer);
        }
        hitPoints = -1.0f;
        if (recovery >= maxRecovery) {
            hitPoints = 100.0f;
            vector<string> playerInfo { uuid, "h", to_string(hitPoints) };
            pushToResponseBuffer("pla", playerInfo, responseBuffer);
            vector<string> burstInfo { uuid, "respawn_burst" };
            pushToResponseBuffer("pb", burstInfo, responseBuffer);
            recovery = 0.0f;
            immuneTime = 3.0f;
            respawn = true;
        }
    }
    // 真的死了，处理玩家重生
    immuneTime -= rate;
    if (immuneTime < __FLT_EPSILON__) { immuneTime = 0.0f; }
    if (gameTick % 30 == 0 || respawn) {
        vector<string> immuneTimeInfo { uuid, "i", to_string(immuneTime) };
        pushToResponseBuffer("pla", immuneTimeInfo, responseBuffer);
    }
    // 处理免疫时间
}

void Player::randomWeapon(queue<LevelAction>& responseBuffer) {
    int randIndex = rand() % weaponList.size();
    string result = weaponList[randIndex];
    weapon.fillWeapon(result, this, responseBuffer);
}

extern unordered_map<string, ProjectileData> bulletDatas;

void Weapon::fillWeapon(string bulletType, Player* targetPlayer) {
    this->bulletType = bulletType;
    maxAmmo = bulletDatas[bulletType].maxAmmo;
    ammo = maxAmmo;
    maxColdDown = bulletDatas[bulletType].coldDown;
    coldDown = 0.0f;
}

void Weapon::fillWeapon(string bulletType, Player* targetPlayer, queue<LevelAction>& responseBuffer) {
    this->bulletType = bulletType;
    maxAmmo = bulletDatas[bulletType].maxAmmo;
    ammo = maxAmmo;
    maxColdDown = bulletDatas[bulletType].coldDown;
    coldDown = 0.0f;
    vector<string> weaponInfo { targetPlayer->uuid, "f", bulletType,
        to_string(targetPlayer->weapon.ammo), to_string(targetPlayer->weapon.maxAmmo) };
    pushToResponseBuffer("pla", weaponInfo, responseBuffer);
}

unordered_map<string, YoukaiInfos> youkaiInfos {
    { "weak_fairy", { 1.6f, 50.0f, 12.0f } },
    { "mult_fairy", { 3.2f, 80.0f, 24.0f } },
    { "spread_fairy", { 4.0f, 150.0f, 30.0f } },
    { "spread_fairy_2", { 4.0f, 150.0f, 30.0f } }
};

// fireMaxColdDown hitPoints lifeTime

void Youkai::fillYoukai(string youkaiType, string uuid, Vector2 pos) {
    this->youkaiType = youkaiType;
    this->uuid = uuid;
    this->pos = pos;
    velocity = Vector2 {0, 0};
    mousePos = Vector2 {0, 0};
    hitPoints = youkaiInfos[youkaiType].hitPoints;
    lifeTime = youkaiInfos[youkaiType].lifeTime;
    fireMaxColdDown = youkaiInfos[youkaiType].fireMaxColdDown;
    fireColdDown = fireMaxColdDown;
}

void Youkai::Update(const float rate, queue<LevelAction> &responseBuffer) {
    Vector2 moveDirection { 0, 0 };
    float currFriction = (friction +
        frictionRate * velocity.returnLength()) * rate;
    if (currFriction >= velocity.returnLength()) {
        velocity = Vector2 { 0, 0 };
    }
    else {
        float currVelocityLength = velocity.returnLength() - currFriction;
        velocity.normalize();
        velocity.mult(currVelocityLength);
    }
    // 妖怪摩擦加速度
    pos.x += velocity.x * rate;
    pos.y += velocity.y * rate;
    // 对于妖怪，只需传入坐标，速度，角度信息由客户端计算，所以这些信息现在不发送
    // 不然我网络带宽要爆了
    // 妖怪位置更新（还有边界判断）
    if (gameTick % 30 == 0) {
        vector<string> posInfo { uuid, to_string(pos.x), to_string(pos.y),
            to_string(velocity.x), to_string(velocity.y), to_string(friction) };
        pushToResponseBuffer("ya", posInfo, responseBuffer);
    }
    lifeTime -= rate;
    fireColdDown -= rate;
}

void Youkai::printYoukaiInfo() {
    printf("%s(%s) -> pos:(%f, %f); velocity:(%f, %f); hitPoints:%f; angle:(%f); existTime:%f \n",
    youkaiType.c_str(), uuid.c_str(), pos.x, pos.y, velocity.x, velocity.y, hitPoints, angle, existTime);
}

Player* getNearestPlayer(Youkai* youkai, unordered_map<string, Player>& players) {
    Player* result = nullptr;
    float distance = __FLT_MAX__;
    if (players.empty()) { return result; }
    for (pair<string, Player> player : players) {
        Player* target = &players[player.second.uuid];
        float currDistance = hypot(abs(target->pos.x - youkai->pos.x),
            abs(target->pos.y - youkai->pos.y));
        if (currDistance < distance) {
            result = target;
            distance = currDistance;
        }
    }
    return result;
};
